<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
</head>
<!--
声明式渲染 :  vue.js的核心允许采用简洁的模板语法来声明式将数据渲染进dom中
<div id="counter">
    Counter: {{ counter }}
</div>

除了文本插值还可以使用指令进行绑定元素
<div id="bind-attribute">
    <span>鼠标悬停查看此处动态绑定的提示信息!</span>
</div>

v-bind attribute 被称为指令,指令带有前缀 v- ,以表示它们是Vue提供的特性 attribute
它们会渲染的Dom上应用特殊的响应式行为,该指令的意思是: 将这个元素节点的 title attribute 和 当前活跃实例的 message property保持一致

处理用户输入
为了让用户和应用进行交互,可以用 v-on 指令添加一个事件监听器
<div>
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转 message</button>
</div>


v-model 指令 可以实现表单输入和应用状态之间的双向绑定
<div id="two-way-binding">
    <p>{{message}}</p>
    <label>
        <input v-model="message"/>
    </label>
</div>

条件与循环 切换一个元素是否显示
<div id="conditional-rendering">
    <span v-if="true">现在你看到我了</span>
</div>
我们可以把数据绑定到DOM文本或attribute,还可以绑定DOM的结构 vue也提供一个强大的过渡效果,可以在vue 插入/更新/移除元素时 自动应用过渡效果


使用 v-for 指令 可以绑定数组的数据来渲染一个项目列表
<div id="list-rendering">
    <ol v-for="todo in todos">
        <li>
            {{todo.text}}
        </li>
    </ol>
</div>

组件化应用构建
组件系统是Vue的另外一个重要概念,因为是一种抽象,允许我们使用小型,独立和通常可复用的组件构建大型应用
在vue中,组件本质上是具有预定义选项的实例,在vue注册组件
// 创建 Vue 应用
const app = Vue.createApp({...})

// 定义名为 todo-item 的新组件
app.component('todo-item', {
    template: `<li>this is a todo </li>`
})

// 挂载 Vue 应用
app.mount('#app')

也可以在另外一个组件模板中使用定义的组件
<div>
<ol>
    // 创建一个 todo-item 组件实例
    <todo-item></todo-item>
</ol>
</div>

但是这样会为每个待办项渲染同样的文本,我们可以将数据从父组件传递到子组件 ,可以修改下组件定义,使其能够接受 prop
app.component('todo-item', {
  props: ['todo'],
  template: `<li>{{ todo.text }}</li>`
})

可以使用 v-bind 指令将待办项传到循环输出的每个组件中
<div>
    <ol>
        // 现在为每个todo-item 提供 todo 对象,todo对象是变量,即内容可以是动态的. 可以为每个组件提供一个 key
        <todo-item
        v-for="item in groceryList"
        v-bind:todo="item"
        v-bind:key="item.id"
        ></todo-item>
    </ol>
</div>

const TodoList = {
  data() {
    return {
      groceryList: [
        { id: 0, text: 'Vegetables' },
        { id: 1, text: 'Cheese' },
        { id: 2, text: 'Whatever else humans are supposed to eat' }
      ]
    }
  }
}
const app = Vue.createApp(TodoList)
app.component('todo-item', {
  props: ['todo'],
  template: `<li>{{ todo.text }}</li>`
})
app.mount('#todo-list-app')



-->
<!--
应用组件实例
每个vue应用都是通过 用 createApp 函数创建一个新的应用实例开始
const app = Vue.createApp({/*选项*/})

这些应用实例都是用来在应用中注册 "全局"组件的

const app = Vue.createApp({})
app.component('SearchInput',SearchInputComponent)
app.directive('focus',FocusDirective)
app.use(LocalePlugin)

应用实例暴露的大多数方法都会返回该同一实例,允许链式
Vue.createApp({})
    .component('SearchInput',SearchInputComponent)
    .directive('focus',FocusDirective)
    .use(LocalePlugin)

根组件 传递给 createApp 的选项用于配置根组件 当挂载应用时,该组件被用作渲染的起点
一个应用需要被挂载到一个 DOM元素
如果 想把一个Vue 应用挂载到 <div id="app"></div>

const RootComponent = { /* 选项 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')

mount 不返回应用本身,它返回的是根组件实例 虽然没有遵循 mvvm模型 因此在文档中会使用 vm 这个变量名表示组件实例
应用组件树
Root Component
└─ TodoList
   ├─ TodoItem
   │  ├─ DeleteTodoButton
   │  └─ EditTodoButton
   └─ TodoListFooter
      ├─ ClearTodosButton
      └─ TodoListStatistics

每个组件将有自己的组件实例 vm 任何时候都可能有多个实例渲染,这个应用中的所有组件实例将共享同一个应用实例

组件实例 property
在 data中定义的 property 是通过组件实例暴露的
const app = Vue.createApp({
    data() {
        return {
            count:4
        }
    }
})
const vm = app.mount('#app')
console.log(vm.count)

用户定义的 property 添加到组件实例中, 如 methods, props, computed,inject和setup .
组件实例的所有 property 都是响应式的, 并且会触发视图更新 都可以在组件的模板中访问
组件暴露了以下内置的 property 如 $data, $el, $attrs, $listeners, $parent, $root, $refs, $slots, $options
这些property 都是一个$ 前缀,以避免与用户定义的property 冲突

生命周期钩子
每个组件在被创建时都要经过一系列的初始化过程 , 设置数据监听,编译模板, 将实例挂载到 DOM 并在数据变化时更新dom,
同时在这个过程中也会运行一些叫做生命周期钩子的函数 , 在实例生命周期的不同阶段调用

created 钩子可以用来在一个实例被创建之后执行代码
Vue.createApp({
    data() {
    return {count:1}
    },
    // 创建钩子函数,在实例被创建之后调用
    created() {
        // this 指向 vm 实例
        console.log(`count is :` + this.count)  // => count is : 1
    }
})

也有一些其他的钩子,在实例生命周期的不同阶段被调用, 如 mounted(挂载), updated(更新) 和 destroyed(杀死) 和 unmounted(卸载)
生命周期钩子的 this 上下文指向调用它的当前活动实例

tip:
不要在选项property 中使用箭头函数, 因为箭头函数会绑定父作用域的 this, 而在函数定义时 this 的值可能已经不是预期了
比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。
因为箭头函数并没有 this，this 会作为变量一直向上级词法作用域查找，直至找到为止，
经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。


生命周期  实例的生命周期

                        app = Vue.createApp(options)
                            app.mount(el)
beforeCreate(创建前): 在实例初始化之后,数据观测和事件/watch 事件配置之前被调用
                            ↓
                        init events & lifecycle

                            ↓
created(创造) : 实例创建完成后被立即调用,此时实例已经完成以下的配置:数据观测,属性和方法的运算,但是$el属性还没有被挂载.

                        init injections & reactivity

                            ↓

                        Has "template"  option ?
beforeMount(安装前) : 实例开始挂载之前被调用.
                            ↓
            ↓ (yes)                                     ↓ (no)
        compile template                        compile el's innerHTML
        into render function *                      as template

mounted(安装好的) : el 被创建的 vm.$el替换,并挂载到实例上之后调用该钩子.
                            ↓
                       create app.$el and
                       replace el with it

beforeUpdate(更新前) : 数据跟新时调用,发生在虚拟DOM打补丁之前
                            ↓

updated(更新) : 组件DOM更新后调用,指令如 v-model和v-show 在此期间也会被重新解析
                       mounted →     when data change

activated(激活的) : 组件激活时调用,如果组件是keep-alive 组件,当组件从缓存中取出并激活时调用
deactivated(停用的) : 组件停用时调用,如果组件是keep-alive 组件,当组件被缓存并停用时调用
beforeUnmount(卸载前) : 实例销毁之前调用
                            ↓
                    when app.unmount() is called

unmounted(未安装的) : 实例销毁后调用
                            ↓
                      unmounted


Composition api 结构api
setup() : 这个函数式Composition API 的入口,在beforeCreate 钩子之前执行,这个函数内部可以使用其他生命周期钩子.
onBeforeMount(): 在 beforeMount 钩子的位置。
onMounted(): 在 mounted 钩子的位置。
onBeforeUpdate(): 在 beforeUpdate 钩子的位置。
onUpdated(): 在 updated 钩子的位置。
onBeforeUnmount(): 在 beforeUnmount 钩子的位置。
onUnmounted(): 在 unmounted 钩子的位置。
onActivated(): 在 activated 钩子的位置。
onDeactivated(): 在 deactivated 钩子的位置。

使用 Options API定义一个简单的组件:
export default {
    data() {
    return {
        message: 'Hello Vue!'
    };
    },
    created() {
        console.log('Component created.')
    },
    mounted() {
        console.log('Component mounted.')
    },
    beforeUpdate() {
        console.log('Component is about to Update.')
    },
    updated() {
        console.log('Component has been updated.')
    }
};

使用 Composition API 定义
javascript

import {ref,onMounted,onBeforeUpdate,onUpdated} from 'vue';

export default {
    setup() {
        const message = ref('Hello Vue!');

        onMounted(() => {
            console.log('Component mounted.')
        });
        onBeforeUpdate(() => {
            console.log('Component is about to Update.');
        });
        onUpdated(() => {
            console.log('Component has been updated.');
        });
    }
    return {message};
}
};

模板语法
Vue.js 使用了基于Html的模板语法,允许开发者声明式地将Dom绑定至底层组件实例的数据.
所有 Vue.js 的模板都是合法的Html 所有能被遵循规范的浏览器和HTML浏览器解析.

在底层的实现上,Vue 将模板编译成虚拟DOM 渲染函数.

插值 -文本
数据绑定最常见的形式就是使用 "Mustache" 语法(双大括号) 的文本插值:
<span>Message: {{msg}}</span>
Mustache 标签将会被替代为对应组件实例中 msg property 的值. 无论何时,绑定的组件实例上 msg property 的值,插值处的内容都会更新.
通过使用 v-once指令 能执行一次性地插值,当数据改变时,插值处的内容不会更新.

<span v-once>这个将不会改变: {{msg}}}</span>

原始Html
双大括号会将数据解释为普通文本,而非HTML代码,为了输出真正的Html,可以使用 v-html 指令:
<p>Using mustaches: {{rawHtml}}</p>
<p>Using v-html directive : <span v-html="rawHtml"></span></p>
这个 span 的内容将会被替换成 property 值 rawHtml. 直接作为 Html -- 会忽略解析 property 值中的数据绑定.
注意,你不能使用 v-html 来复合局部模板,因为Vue不是基于字符串的模板引擎.反之,对用户界面ui 组件更适合作为可重用和可组合的基本单位.

tip
在你的站点上动态渲染任意的html是非常危险的, 因为它很容易导致 XSS 攻击模板语法 - 图1 (opens new window)。请只对可信内容使用 HTML 插值，绝不要将用户提供的内容作为插值。

Attribute
Mustache 语法不能在 Html attribute 中使用,然而 可以使用 v-bind 指令:
<div v-bind:id="dynamicId"></div>

对于 布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同
<button v-bind:disabled="isButtonDisabled">按钮</button>
如果 isButtonDisabled 的值为 null 或 undefined,则disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中.


使用 javaScript 表达式
迄今为止,在模版中,我们一直都只绑定简单的property键值.但实际上,对于所有的数据绑定,Vue.js都提供了完全的JavaScript 表达式支持.

{{number + 1}}
{{ok ? 'YES':'NO' }}
{{message.split('').reverse().join('')}}
<div v-bind:id="'list-'+id"></div>

这些表示式会在当前活动实例的数据作用域下作为 JavaScript 被解析.有个限制就是,每个绑定都只能包含单个表达式,
这是语句,不是表达式:
{{var a=1}}

流控制也不会生效,请使用三元表达式
{{if (ok) {}}


指令 是带有 v- 前缀的特殊attribute. 指令attribute 的值预期的单个 JavaScript 表达式.
v-for 和 v-on ,指令的职责是当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM.

<P v-if="seen">现在你看到我了</P>
这里 v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素.

参数
一些指令能够接收一个参数,在指令名称之后以冒号表示. 例如 v-bind: 指令可以用于响应式地更新 HTML attribute.
<a v-bind:href="url">...</a>
在这里href 是参数,告知 v-bind 指令将元素的 href attribute 更新为表达式 url 的值.

v-on 指令,用于监听DOM事件
<a v-on:click="doSomething"></a>
这里参数是监听的事件名

动态参数 也可以在指令参数中使用 JavaScript 表达式.方法是用方括号起来
参数表达式的写法存在一些约束,如之后的对动态参数表达式的约束
<a v-bind:[attributeName]="url">...</a>
这里 attributeName 会被作为一个javaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用.
比如 组件实例 有一个 data property attributeName, 其值为 'href', 那么这个绑定将等价与 v-bind:href="url".

可以使用动态参数为一个动态的事件名绑定处理函数
<a v-on:[eventName]="doSomething">...</a>
当 eventName 的值是 "focus"时,v-on:[eventName] 将等价与 v-on:focus

修饰符 是以半角句号 . 指明的特殊后缀 , 用于指出一个指令应该以特殊方式绑定
.prevent 修饰符告诉 v-on指令 对触发的事件调用 event.preventDefault()
<from v-on:submit.prevent="onSubmit"></form>


缩写 v- 前缀作为一种视觉提示,用来识别模板中 Vue特定的attribute. 当使用Vue.js 为现有标签添加动态行为时,
v- 前缀可以省略 ,因此 Vue 为 v-bind 和 v-on 这两个最常用的指令 提供了缩写

v-bind 缩写
完整语法
<a v-bind:href="url">...</a>

缩写
<a :href="url">...</a>

动态参数的缩写
<a :[key]="url"></a>

v-on 缩写
完整语法
<a v-on:click="doSomething">...</a>
缩写
<a @click="doSomething">...</a>
动态参数的缩写
<a @[event]="doSomething">...</a>

看起来可能与普通的 HTML 略有不同,但 : 与 @ 对于attribute 名来说都是合法字符
对动态参数表达式约定
动态参数表达式有一些语法约束，因为某些字符，如空格和引号，放在 HTML attribute 名里是无效的。例如：
 这会触发一个编译警告
<a v-bind:['foo' + bar]="value"> ... </a>

计算属性
模板中放入太多逻辑会让模板过重


-->
<!--
计算属性  模版内的表达式非常便利,设计的初衷是用于简单运算的.
嵌套数组对象

Vue.createApp({
    data(){
        return {
            author : {
                name:'jin',
                books:[
                    'Vue 2 - Advanced Guide',
                    'Vue 3 - Basic Guide',
                    'Vue 4 - The Mystery',
                ]
            }
        }
    }
})

根据 author 是否已经有值来显示不同的消息
<div id="computed-basics">
    <p>Has published books:</p>
    <span>{{author.books.length > 0 ? 'Yes':'No'}}</span>
</p>div>


基本例子
<div id="computed-basics">
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</div>

Vue.createApp({
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // 计算属性的 getter
    publishedBooksMessage() {
      // `this` points to the vm instance
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}).mount('#computed-basics')


Result:
这里声明了一个计算属性 publishedBooksMessage，尝试更改应用程序 data 中 books 数组的值,相应的publishedBooksMessage 的值也会相应变化。
你可以像普通属性一样将数据绑定到模板中的计算属性。Vue 知道 vm.publishedBookMessage 依赖于 vm.author.books，因此当 vm.author.books 发生改变时，
所有依赖 vm.publishedBookMessage 绑定也会更新。而且最妙的是我们已经声明的方式创建了这个依赖关系：计算属性的 getter 函数没有副作用，这使得更易于测试和理解。

计算属性缓存 vs 方法
<p>{{ calculateBooksMessage() }}</p>

// 在组件中
methods: {
  calculateBooksMessage() {
    return this.author.books.length > 0 ? 'Yes' : 'No'
  }
}

我们可以将同一函数定义为一个方法而不是一个计算属性.两种方式的最终结果确实是完全相同的.
然而不同是计算属性是基于它们的反应依赖关系缓存的.计算属性只在相关响应式依赖发生改变时它们才会重新求值.这就以为着 author.books 的变化不会触发计算属性的更新，
多次访问 publishedBooksMessage 计算属性会立即返回之前的计算结果,不必再执行函数

相比之下，每当触发重新渲染时，调用方法将总会再次执行函数。

我们为什么需要缓存？假设我们有一个性能开销比较大的计算属性 list，它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 list。如果没有缓存，我们将不可避免的多次执行 list 的 getter！如果你不希望有缓存，请用 method 来替代。

计算属性的 Setter
计算属性默认只有 getter，不过在需要时你也可以提供一个 setter：
// ...
computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...
现在再运行 vm.fullName = 'John Doe' 时，setter 会被调用，vm.firstName 和 vm.lastName 也会相应地被更新。

侦听器
虽然计算属性在大多数情况下更合适，但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法，来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时，这个方式是最有用的。

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question" />
  </p>
  <p>{{ answer }}</p>
</div>

 因为 AJAX 库和通用工具的生态已经相当丰富，Vue 核心代码没有重复
 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script>
    const watchExampleVM = Vue.createApp({
        data() {
            return {
                question: '',
                answer: 'Questions usually contain a question mark. ;-)'
            }
        },
        // 监听器
        watch: {
            // whenever question changes, this function will run
            question(newQuestion, oldQuestion) {
                if (newQuestion.indexOf('?') > -1) {
                    this.getAnswer()
                }
            }
        },
        methods: {
            getAnswer() {
                this.answer = 'Thinking...'
                axios
                    .get('https://yesno.wtf/api')
                    .then(response => {
                        this.answer = response.data.answer
                    })
                    .catch(error => {
                        this.answer = 'Error! Could not reach the API. ' + error
                    })
            }
        }
    }).mount('#watch-example')
</script>

结果 : 在这个示例中,使用 watch 选项允许我们执行异步操作 访问一个 api 限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态,这些都是计算属性无法实现的。


-->
<!--
class 与 Style 绑定
操作元素的class 列表和内联样式是数据绑定的一个常见需求。因为它们都是attribute ,所以我们可以用 v-bind 处理它们: 只需要通过 表达式计算出字符串结果即可
不过 字符串拼接麻烦易错,因此，在将 v-bind 用于 class 和 style 时，Vue.js 做了专门的增强。表达式结果的类型除了字符串之外，还可以是对象或数组。

绑定 Html class
对象语法
我们可以传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class.
<div v-bind:class="{ active: isActive }"></div>
<div :class="{ active: isActive }"></div>
上面的语法表示 active 这个class 存在与否将取决于 数据 property isActive 的值。

也可以在对象中传入更多字段来动态切换多个 class。例如： :class 指令也可以与普通的 class attribute 混合使用：
<div>
    class="text-center"
    :class="{ active: isActive,'text-danger': hasError}"
</div>
如下 data 属性：
data() {
  return {
    isActive: true,
    hasError: false
  }
}
渲染结果
<div class="text-center active"></div>
当 isActive 或 hasError 的值改变，class 列表将相应地更新。 如果 hasError 的值为 true，class 列表将变为 text-center text-danger。

绑定的数据对象不必 内联定义在模板里
<div :class="classObject"></div>

data 属性
data() {
  return {
    classObject: {
      active: true,
      'text-danger': false
    }
  }
}

渲染的结果和上面一样,我们也可以在这里绑定一个返回对象的计算属性.这里是一个常用且强大的模式
data() {
  return {
    isActive: true,
    error: null
  }
},
computed: {
  classObject() {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}

数组语法 我们可以把一个数组传给 :class,以应用一个 class 列表
<div :class="[activeClass, errorClass]"></div>
data属性
data() {
  return {
    activeClass: 'active',
    errorClass: 'text-danger'
  }
}

渲染结果
<div class="active text-danger"></div>

如果想根据条件切换列表的 class 可以使用三元表达式
<div :class="[isActive ? activeClass : '', errorClass]"></div>
这样写将始终添加 errorClass,但是只有在 isActive 为 true 时才应用 activeClass。

当有多个条件 class 时这样写有些繁琐.所以在数组语法中也可以使用对象语法：
<div :class="[{ active: isActive }, errorClass]"></div>

在 组件上使用 eg: 如果声明了这个组件:
const app = Vue.createApp({})
app.component('my-component', {
  template: `<p class="foo bar">Hi!</p>`
})

然后 在使用它的时候添加一些 class
<div id="app">
    <my-component class="baz boo"></my-component>
</div>
HTML 将被渲染为
<p class="foo bar baz boo">Hi</p>

对于带数据绑定 class也同样适用
<my-component :class="{ active: isActive }"></my-component>

当 isActive 为 true 时，渲染结果如下：
<p class="foo bar active">Hi</p>

如果组件有多个根元素 需要定义那个部分将接收这个类 可以使用 $attrs 组件属性执行操作
<div id="app">
  <my-component class="baz"></my-component>
</div>

const app = Vue.createApp({})
app.component('my-component', {
  template: `
    <p :class="$attrs.class">Hi!</p>
    <span>This is a child component</span>
  `
})

绑定内联样式
对象语法
:style 的对象语法十分直观——看着非常像 CSS，但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case，记得用引号括起来) 来命名：

<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data() {
  return {
    activeColor: 'red',
    fontSize: 30
  }
}

直接绑定到一个样式对象通常更好，这会让模板更清晰：
<div :style="styleObject"></div>
data() {
  return {
    styleObject: {
      color: 'red',
      fontSize: '13px'
    }
  }
}
同样的，对象语法常常结合返回对象的计算属性使用。

数组语法
:style 的数组语法可以将多个样式对象应用到同一个元素上：

<div :style="[baseStyles, overridingStyles]"></div>
自动添加前缀
在 :style 中使用需要 (浏览器引擎前缀) vendor prefixesClass 与 Style 绑定 - 图2 (opens new window) 的 CSS property 时，如 transform，Vue 将自动侦测并添加相应的前缀。

多重值
可以为 style 绑定中的 property 提供一个包含多个值的数组，常用于提供多个带前缀的值，例如：
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中，如果浏览器支持不带浏览器前缀的 flexbox，那么就只会渲染 display: flex。







-->
<!--
条件渲染
v-if  指令用于条件性渲染一块内容,这块内容只会在指令的表达式返回 true 时被渲染。
<h1 v-if="awesome">Vue is awesome!</h1>
也可以 v-else 指令 添加一个 else块
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

在 <template> 元素上使用 v-if条件渲染分组
因为 v-if 是一个指令,所以必须将它添加到一个元素上.但是如果想切换多个元素嘞,此时可以把一个 <template> 元素当做不可见的包装元素
并在上面使用 v-if 最终的渲染结果将不包含 <template>
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-else 指令
<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-else 元素必须紧跟在 v-if 或者 v-else-if 元素后面。否则它将不会被识别
v-else-if 顾名思义,充当 v-if 的 else-if块 可以连续使用
<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>
类似于 v-else，v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

v-show 另外一个是用于根据条件展示元素的选项是 v-show指令
<h1 v-show="ok">hello!</h1>
不同的是 v-show 的元素始终会被渲染并保留在Dom中 v-show 只是把元素css属性display设置为none
v-if vs v-show
v-if 是“真正”的条件渲染，因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的：如果在初始渲染时条件为假，则什么也不做——直到条件第一次变为真时，才会开始渲染条件块。

相比之下，v-show 就简单得多——不管初始条件是什么，元素总是会被渲染，并且只是简单地基于 CSS 进行切换。

一般来说，v-if 有更高的切换开销，而 v-show 有更高的初始渲染开销。因此，如果需要非常频繁地切换，则使用 v-show 较好；如果在运行时条件很少改变，则使用 v-if 较好。

v-if 与 v-for 一起使用
提示

不推荐同时使用 v-if 和 v-for






-->
<!--
列表渲染
用 v-for 把一个数组对应一组元素
可以用 v-for 指令基于一个数组来渲染一个列表,v-for 指令需要使用 item in items 形式的特殊语法,其中items 是源数据数组 而 item则是被迭代的数组元素的别名
<ul id="array-rendering">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>

Vue.createApp({
  data() {
    return {
      items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
  }
}).mount('#array-rendering')

在 v-for块中 我们可以访问所有父作用域的 property. v-for 还可以一个可选的第二个参数,即当前项的索引
<ul id="array-with-index">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>

Vue.createApp({
  data() {
    return {
      parentMessage: 'Parent',
      items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
  }
}).mount('#array-with-index')

你也可以用 of 替代 in 作为分隔符，因为它更接近 JavaScript 迭代器的语法：
<div v-for="item of items"></div>

你也可以用 v-for 来遍历一个对象的 property。
<ul id="v-for-object" class="demo">
  <li v-for="value in myObject">
    {{ value }}
  </li>
</ul>

Vue.createApp({
  data() {
    return {
      myObject: {
        title: 'How to do lists in Vue',
        author: 'Jane Doe',
        publishedAt: '2016-04-10'
      }
    }
  }
}).mount('#v-for-object')

提供第二个的参数为 property 名称 (也就是键名 key)：
<li v-for="(value, name) in myObject">
  {{ name }}: {{ value }}
</li>

第三个参数作为索引：
<li v-for="(value, name, index) in myObject">
  {{ index }}. {{ name }}: {{ value }}
</li>
在遍历对象时，会按 Object.keys() 的结果遍历，但是不能保证它在不同 JavaScript 引擎下的结果都一致。

维护状态
当 Vue 正在更新使用 v-for 渲染的元素列表时，它默认使用“就地更新”的策略。如果数据项的顺序被改变，Vue 将不会移动 DOM 元素来匹配数据项的顺序，而是就地更新每个元素，并且确保它们在每个索引位置正确渲染。

这个默认的模式是高效的，但是只适用于不依赖子组件状态或临时 DOM 状态 (例如：表单输入值) 的列表渲染输出。

为了给 Vue 一个提示，以便它能跟踪每个节点的身份，从而重用和重新排序现有元素，你需要为每项提供一个唯一 key attribute：
<div v-for="item in items" :key="item.id">
   content
</div>

Vue 将被侦听的数组的变更方法进行了包裹，所以它们也将会触发视图更新。这些被包裹过的方法包括：

push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你可以打开控制台，然后对前面例子的 items 数组尝试调用变更方法。比如 example1.items.push({ message: 'Baz' })。
替换数组
变更方法，顾名思义，会变更调用了这些方法的原始数组。相比之下，也有非变更方法，例如 filter()、concat() 和 slice()。它们不会变更原始数组，而总是返回一个新数组。当使用非变更方法时，可以用新数组替换旧数组：





-->
<body>
<div id="list-rendering">
    <ol v-for="todo in todos">
        <li>
            {{todo.text}}
        </li>
    </ol>
</div>

<script>
    const ListRendering = {
        data() {
            return {
                todos: [
                    { text: 'Learn JavaScript' },
                    { text: 'Learn Vue' },
                    { text: 'Build something awesome' }
                ]
            }
        }
    }
    Vue.createApp(ListRendering).mount('#list-rendering')
</script>

<!--<div id="conditional-rendering">-->
<!--    <span v-if="true">现在你看到我了</span>-->
<!--</div>-->

<!--<script>-->
<!--    const ConditionalRendering = {-->
<!--        data() {-->
<!--            return {-->
<!--                seen: true-->
<!--            }-->
<!--        }-->
<!--    }-->
<!--    Vue.createApp(ConditionalRendering).mount('#conditional-rendering')-->
<!--</script>-->

<!--<div id="counter">-->
<!--    Counter: {{ counter }}-->
<!--</div>-->

<!--<div id="bind-attribute">-->
<!--    <span>鼠标悬停查看此处动态绑定的提示信息!</span>-->
<!--</div>-->
<!--<div id="two-way-binding">-->
<!--    <p>{{message}}</p>-->
<!--    <label>-->
<!--        <input v-model="message"/>-->
<!--    </label>-->
<!--</div>-->
<!--<script>-->
<!--    const TwoWayBinding = {-->
<!--        data() {-->
<!--            return {-->
<!--                message: 'Hello Vue!'-->
<!--            }-->
<!--        }-->
<!--    }-->
<!--    Vue.createApp(TwoWayBinding).mount('#two-way-binding')-->
<!--</script>-->

<!--<div>-->
<!--    <p>{{ message }}</p>-->
<!--    <button v-on:click="reverseMessage">反转 message</button>-->
<!--</div>-->
<!--<script>-->
<!--    const EventHandling = {-->
<!--        data() {-->
<!--            return {-->
<!--                message: 'Hello Vue.js!'-->
<!--            }-->
<!--        },-->
<!--        methods: {-->
<!--            reverseMessage() {-->
<!--                this.message = this.message-->
<!--                    .split('')-->
<!--                    .reverse()-->
<!--                    .join('')-->
<!--            }-->
<!--        }-->
<!--    }-->
<!--    Vue.createApp(EventHandling).mount('#event-handling')-->
<!--</script>-->

<!--<script>-->
<!--    const AttributeBinding = {-->
<!--        data() {-->
<!--            return {-->
<!--                message: 'You loaded this page on ' + new Date().toLocaleString()-->
<!--            }-->
<!--        }-->
<!--    }-->
<!--    Vue.createApp(AttributeBinding).mount('#bind-attribute')-->
<!--</script>-->

<!--<script>-->
<!--    const Counter = {-->
<!--        data() {-->
<!--            return {-->
<!--                counter: 0-->
<!--            }-->
<!--        },-->
<!--        methods() {-->
<!--            setInterval(() => {-->
<!--                this.counter++-->
<!--        },1000)-->
<!--        }-->
<!--    }-->
<!--    Vue.createApp(Counter).mount('#counter')-->
<!--</script>-->
</body>
</html>